package logic;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import weka.classifiers.Classifier;
import weka.core.Attribute;
import weka.core.Capabilities;
import weka.core.Capabilities.Capability;
import weka.core.Instance;
import weka.core.Instances;

public abstract class MushroomClassifier extends Classifier {
    public boolean[] usedAttrs = new boolean[MushroomInfo.NUM_ATTRIBUTES];
    public HashMap<Character, Double> classValues = new HashMap<Character, Double>();
    
    public MushroomClassifier() {
        
    }
    
    public void initClassValues(Attribute clsAttr) {
        for (int i = 0; i < clsAttr.numValues(); i++) {
            classValues.put(clsAttr.value(i).charAt(0), (double)i);
        }
    }
    
    public boolean[] getUsedAttrs(Instance i) {
        Enumeration en = i.enumerateAttributes();
        HashMap<String, Integer> atrIdx = MushroomInfo.singleton.ATTRIBUTE_INDEX;
        boolean[] used = new boolean[MushroomInfo.NUM_ATTRIBUTES];
        
        for (int j = 0; j < used.length; j++) {
            used[j] = false;
        }
        
        while (en.hasMoreElements()) {
            Attribute at = (Attribute)en.nextElement();
            used[atrIdx.get(at.name())] = true;
        }
        
        used[atrIdx.get(i.classAttribute().name())] = true;
        
        return used;
    }
    
    public void initUsedAttrs(Instance i) {
        usedAttrs = getUsedAttrs(i);
    }
    
    public Instances[] percentageSplit(Instances insts, int percent) {
        Instances[] result = new Instances[2];
        for (int i = 0; i < result.length; i++) {
            result[i] = new Instances(insts);
        }
        
        int numInst = insts.numInstances();
        int numTraining = (numInst*percent)/100;
        int numTest = numInst - numTraining;
        ArrayList<Integer> toTraining = new ArrayList<Integer>();
        
        for (int i = 0; i < numTraining; i++) {
            int rand = (int)(Math.random()*numInst);
            while (toTraining.contains(rand)) {
                if (rand == (numInst - 1))
                    rand = 0;
                else
                    rand++;
            }
            insertSequential(toTraining, rand);
        }
        
        for (int i = numInst - 1; i >= 0; i--) {
            if (toTraining.contains(i))
                result[1].delete(i);
            else
                result[0].delete(i);
        }
        
        return result;
    }
    
    public void insertSequential(ArrayList<Integer> ints, int val) {
        for (int i = 0; i < ints.size(); i++) {
            if (val < ints.get(i)) {
                ints.add(i, val);
                return;
            }
        }
        ints.add(val);
    }
    
    @Override
    public Capabilities getCapabilities() {
        Capabilities result = new Capabilities(this);
        
        result.enable(Capability.NOMINAL_ATTRIBUTES);
        result.enable(Capability.MISSING_VALUES);
        
        result.enable(Capability.NOMINAL_CLASS);
        
        return result;
    }
    
    @Override
    public double classifyInstance(Instance i) {
       return classValues.get(classify(i));
    }
    
    public double accuracy(Instances insts) {
        int count = 0;
        
        System.out.println();
        Enumeration enu = insts.enumerateInstances();
        while (enu.hasMoreElements()) {
            Instance in = (Instance)enu.nextElement();
            try {
                if (classifyInstance(in) == in.classValue())
                    count++;
            } catch (Exception e) {
                System.err.println("Exception in MushroomClassifier.accuracy()");
            }
        }
        
        return (double)count/insts.numInstances();
    }
    
    public Instance createInstance(char[] str, Instances dataset) {
        StringBuilder s = new StringBuilder();
        for (char c : str) {
            s.append(c).append(",");
        }
        
        String st = s.toString();
        st = st.substring(0, s.toString().length() - 1);
        return createInstance(st, dataset);
    }
    public Instance createInstance(String str, Instances dataset) {
        String[] splits = str.split(",");
        Instance in = new Instance(dataset.firstInstance());
        in.setDataset(dataset);
        
        for (int i = 0; i < in.numAttributes() - 1; i++) {
            in.setValue(i, splits[i]);
        }
        
        return in;
    }
    
    public abstract char classify(Instance in);
    public abstract char classify(Instance in, boolean[] selectionAttrs);
}
